﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Kokomo.Lexical.Expressions;

namespace Kokomo.PeInspector.Reflection.Disassembly.Flow
{
	class SwitchDefaultFlow : FlowAnalyzer
	{
		public SwitchDefaultFlow(
			SwitchFlowAnalyzer switchFlow,
			int blockStartOffset,
			int blockEndOffset
			)
			: base(switchFlow, blockStartOffset, blockEndOffset, blockStartOffset, blockEndOffset)
		{
			this._switchFlow = switchFlow;
		}

		private SwitchFlowAnalyzer _switchFlow;

		protected override void AnalyzeSwitch(MsilSwitch @switch)
		{
			this._switchFlow.BuildCaseTable(@switch.TargetOffsets, this.Context.PopStackEntry());
			this.Context.Advance();
		}
		protected override void AnalyzeLocalBranch(MsilBranch branch)
		{
			//if (this._switchFlow.DefaultCaseStartOffset.HasValue) throw new InvalidOperationException("Switch block already has default case label");

			this._switchFlow.DefaultCaseStartOffset = branch.TargetOffset;

			this.Context.Advance();
		}
		protected override void AnalyzeLocalConditionalBranch(MsilBranch branch)
		{
			MsilStackEntry conditionEntry = this.Context.PopStackEntry();
			BinaryOperationExpression binary = conditionEntry.Expression as BinaryOperationExpression;
			if (binary == null) throw new InvalidOperationException(Messages.InvalidSwitchPreambleMessage);
			if (binary.Operator != ExpressionOperator.Equality) throw new InvalidOperationException(Messages.InvalidSwitchBranchMessage);

			int caseLabel;
			ConstantExpression constantLabel = binary.Right as ConstantExpression;
			if (constantLabel != null)
			{
				caseLabel = Convert.ToInt32(constantLabel.Value);
			}
			else
			{
				FieldReferenceExpression fieldLabel = binary.Right as FieldReferenceExpression;
				if (fieldLabel != null)
				{
					caseLabel = Convert.ToInt32(fieldLabel.Field.GetRawConstantValue());
				}
				else
				{
					throw new InvalidOperationException("Unrecognized label expression");
				}
			}
			this._switchFlow.CaseTable[caseLabel] = branch.TargetOffset;

			this.Context.Advance();
		}
	}
}
